package com.powernode.controller;

import org.apache.rocketmq.client.producer.SendCallback;
import org.apache.rocketmq.client.producer.SendResult;
import org.apache.rocketmq.spring.core.RocketMQTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.concurrent.atomic.AtomicInteger;

@RestController
public class SecKillController {

    @Autowired
    private StringRedisTemplate redisTemplate;

    @Autowired
    private RocketMQTemplate rocketMQTemplate;

    // 原子类(CAS) java层面是无锁的,底层是有锁的 -- 原子性(安全)
    AtomicInteger userIdAt = new AtomicInteger(0);

    /**
     * 思路: 1.去重;    2.库存预扣减;    3.消息放入mq
     * @param goodsId
     * @param userId 真实项目要做登录,不需要传递
     * @return string
     */
    @GetMapping("/seckill")
    public String doSecKill(Integer goodsId/*, Integer userId*/) {
        int userId = userIdAt.incrementAndGet();    // 线程
        String uk = userId + "-" + goodsId;

        // setIfAbsent = setnx
        Boolean flag = redisTemplate.opsForValue().setIfAbsent("uk:" + uk, "");
        if (!flag) {
            return "您已经参与过该商品的抢购,请参与其他商品!";
        }

        // todo 库存运扣减   假设redis存入goodsId
        // 传统写法:
        //  注意: 先查再改再更新是不安全的操作 -- 解决方案: redis直接减库存(redis是单线程,安全)
        //  i--; 不安全; 原因: 线程(每个线程都是独立,互不影响)从主存读取变量i,通过值引擎运算后返回给主存中的变量i;若其中一个线程读取运算还没返回变量值,另一个线程又开始读取原变量值,获取到的结果不理想;
        /*String s = redisTemplate.opsForValue().get(goodsId);
        int count = Integer.parseInt(s);
        count--;
        if (count < 0) {
            return "该商品已售罄!";
        }
        redisTemplate.opsForValue().set(String.valueOf(goodsId), String.valueOf(count));*/

        // 优化：线程安全,redis操作数据层面是单线程的
        Long count = redisTemplate.opsForValue().decrement("goodsId:" + goodsId);
        if (count < 0) {
            return "该商品已售罄!";
        }

        // mq异步处理
        rocketMQTemplate.asyncSend("seckillTopic", uk, new SendCallback() {
            @Override
            public void onSuccess(SendResult sendResult) {
                System.out.println("发送成功");
            }

            @Override
            public void onException(Throwable throwable) {
                System.out.println("发送失败: " + throwable.getMessage());
                System.out.println("用户id: " + userId + "商品id: " + goodsId);
            }
        });
        return "正在抢购中,请稍后查看";
    }
}
